#ifndef BASIC_HPP_INCLUDED
#define BASIC_HPP_INCLUDED

#include "tool_math.hpp"

namespace shooter
{
namespace basic
{
  /**
   * @breif  basic struct to represent point.
   */
  template <class T = double>
  struct unit_coor_t
  {
    T x_, y_, z_; // in unit of meter.

    /* Constructor */
    unit_coor_t():x_(0), y_(0), z_(0){}
    unit_coor_t(T x0, T y0, T z0): x_(x0), y_(y0), z_(z0){}

    unit_coor_t(const unit_coor_t & copy): x_(copy.x_), y_(copy.y_), z_(copy.z_){}
    const unit_coor_t & operator=(const unit_coor_t & copy)
    {
      x_ = copy.x_; y_ = copy.y_; z_ = copy.z_;
      return (*this);
    }
    virtual ~unit_coor_t(){}

    /* Overload operator */
    const unit_coor_t operator+(const unit_coor_t & ele) const
    {
      unit_coor_t tmp(this->x_+ele.x_, this->y_+ele.y_, this->z_+ele.z_);
      return tmp;
    }
    const unit_coor_t operator-(const unit_coor_t & ele) const
    {
      unit_coor_t tmp(this->x_-ele.x_, this->y_-ele.y_, this->z_-ele.z_);
      return tmp;
    }
    const unit_coor_t operator*(double ratio) const
    {
      unit_coor_t tmp(x_*ratio, y_*ratio, z_*ratio);
      return tmp;
    }
    bool operator==(const unit_coort_t & ele) const
    {
      return (x_==ele.x_) && (y_==ele.y_) && (z_==ele.z_);
    }
    bool operator!=(const unit_coort_t & ele) const
    {
      return !((x_==ele.x_) && (y_==ele.y_) && (z_==ele.z_));
    }
  };

  /**
   * @breif  basic struct to represent vector.
   */
  template <class T = double>
  struct unit_vector_t
  {
    T x_, y_, z_; // in unit of meter.

    /* Constructor */
    unit_vector_t(): x_(0.0), y_(0.0), z_(0.0){}
    unit_vector_t(T x0, T y0, T z0): x_(x0), y_(y0), z_(z0){}
    unit_vector_t(T v0, T alpha0, T beta0, int dummy)
    {
      this->x_ = v0 * std::cos(beta0) * std::sin(alpha0);
      this->y_ = v0 * std::cos(beta0) * std::cos(alpha0);
      this->z_ = v0 * std::sin(beta0);
      dummy = dummy; // not used, just to classify overload function.
    }

    unit_vector_t(const unit_coor_t<T> & copy)
    {
      this->x_ = copy.x_; this->y_ = copy.y_; this->z_ = copy.z_;
    }
    unit_vector_t(const unit_vector_t<T> & copy)
    {
      this->x_ = copy.x_; this->y_ = copy.y_; this->z_ = copy.z_;
    }
    const unit_vector_t & operator=(const unit_coor_t<T> & copy)
    {
      this->x_ = copy.x_; this->y_ = copy.y_; this->z_ = copy.z_;
      return (*this);
    }
    const unit_vector_t & operator=(const unit_vector_t & copy)
    {
      this->x_ = copy.x_; this->y_ = copy.y_; this->z_ = copy.z_;
      return (*this);
    }
    ~unit_vector_t(){}

    friend unit_vector_t tool_math::cal_crossProduct<unit_vector_t>(const unit_vector_t & edge1, const unit_vector_t & edge2);
    friend double tool_math::cal_dotProduct<unit_vector_t>(const unit_vector_t & edge1, const unit_vector_t & edge2);

    /* Overload operator */
    const unit_vector_t operator+(const unit_vector_t & ele) const
    {
      unit_vector_t tmp(this->x_+ele.x_, this->y_+ele.y_, this->z_+ele.z_);
      return tmp;
    }
    const unit_vector_t operator-(const unit_vector_t & ele) const
    {
      unit_vector_t tmp(this->x_-ele.x_, this->y_-ele.y_, this->z_-ele.z_);
      return tmp;
    }
    const unit_vector_t operator*(double ratio) const
    {
      unit_vector_t tmp(x_*ratio, y_*ratio, z_*ratio);
      return tmp;
    }
    const unit_vector_t operator^(const unit_vector_t & ele) const // Cross product.
    {
      return tool_math::cal_crossProduct((*this), ele);
    }
    double operator*(const unit_vector_t & ele) const // Dot product.
    {
      return tool_math::cal_dotProduct((*this), ele);
    }

    /* Others */
    T length() const
    {
      T xy = tool_math::cal_mag(this->x_, this->y_);
      return tool_math::cal_mag(xy, this->z_);
    }

    T alpha() const
    {
      return (this->x_ == 0)? tool_math::basic_parms<0>::C_Pi2: std::atan(this->y_ / this->x_);
    }

    T beta() const
    {
      T xy = tool_math::cal_mag(this->x_, this->y_);
      return (xy == 0)? tool_math::basic_parms<0>::C_Pi2: std::atan(this->z_ / xy);
    }
  };

  template <class T = double>
  class

  template <class T = double>
  class point_t
  {
  public:
    /* Constructor */
    point_t(): coor_(), speed_(){}
    point_t(T x0, T y0, T z0): coor_(x0, y0, z0), speed_(){}
    point_t(T c_x, T c_y, T c_z, T s_x, T s_y, T s_z): coor_(c_x, c_y, c_z), speed_(s_x, s_y, s_z){}

    point_t(const point_t & copy)
    {
      coor_ = copy.coor();
      speed_ = copy.speed();
    }
    virtual ~point_t(){}

    /* Others */
    unit_coor_t<T> coor() const
    {
      return coor_;
    }
    unit_vector_t<T> speed() const
    {
      return speed_;
    }
    void set_speed(T x0, T y0, T z0)
    {
      speed_.x_ = x0; speed_.y_ = y0; speed_.z_ = z0;
    }
    void set_speed(const unit_vector_t<T> & ele)
    {
      speed_ = ele;
    }
    /**
     * @breif  To simulate real-world, you can not use func like 'set_coor' to change coordinate,
     *         instead you can only use \code'refresh_coor' where \code'speed_' variable decide how
     *         refresh.
     *
     * @input  dt             Delta time, in unit of second.
     *
     * @note   (1) coor_new = coor_old + speed*dt;
     */
    void refresh_coor(double dt)
    {
      coor_.x_ += speed_.x_*dt;
      coor_.y_ += speed_.y_*dt;
      coor_.z_ += speed_.z_*dt;
    }

  private:
    unit_coor_t<T> coor_;
    unit_vector_t<T> speed_;
    /** @note No assignment! */
    const point_t & operator=(const point_t<T> & copy){}
  };

  template <class T = double>
  class line_t
  {
  public:
    /* Constructor */
    line_t(): point_beg_(), point_end_(), vector_(){}
    line_t(const point_t<T> & p1, const point_t<T> & p2): point_beg_(p1), point_end_(p2), vector_(point_beg_.coor()-point_end_.coor()){}
    line_t(T x1, T y1, T z1, T x2, T y2, T z2): point_beg_(x1, y1, z1), point_end_(x2, y2, z2), vector_(point_end_.coor()-point_beg_.coor()){}
    line_t(const line_t & copy): point_beg_(copy.point_beg_), point_end_(copy.point_end_), vector_(point_end_.coor()-point_beg_.coor()){}
    ~line_t(){}

    /* Others */
    const point_t<T> point_beg() const{return point_beg_;}
    const point_t<T> point_end() const{return point_end_;}
    const unit_vector_t<T> vector() const{return vector_;}
    T length() const
    {
      unit_vector_t<T> tmp;
      tmp = point_beg_.coor() - point_end_.coor();
      return tmp.length();
    }

  private:
    /** @note No assignment ! */
    const line_t & operator=(const line_t & copy){}

    point_t<T> point_beg_, point_end_;
    unit_vector_t<T> vector_;
  };

  template <class T>
  double cal_distance(const point_t<T> & point1, const point_t<T> & point2)
  {
    unit_vector_t<T> v;
    v = point1.coor() - point2.coor();
    return v.length();
  }

  template <class T>
  double cal_distance(const point_t<T> & point, const line_t<T> & line)
  {
    if(line.length() == 0)
    {
      return 0;
    }
    unit_vector_t<> tmp1 = point.coor() +line.point_beg().coor();
    unit_vector_t<> tmp2 = point.coor() + line.point_end().coor();
    unit_vector_t<> tmp3 = tmp1 ^ tmp2;
    return tmp3.length() / line.length();
  }

  template <class T>
  double cal_angle(const line_t<T> & line1, const line_t<T> & line2)
  {
    if((line1.length() == 0) || line2.length() == 0)
    {
      return 0;
    }
    double dp = tool_math::cal_dotProduct(line1.vector(), line2.vector());
    return std::acos(dp / (line1.length() *line2.length()));
  }

} // namespace basic
} // namespace shooter



#endif
